package org.loofer.ext.utils.byteunits;

import java.text.DecimalFormat;
import java.text.NumberFormat;

public enum DecimalByteUnit implements ByteUnit {
    BYTES {
        public long convert(long sourceCount, DecimalByteUnit sourceUnit) {
            return sourceUnit.toBytes(sourceCount);
        }

        public long toBytes(long count) {
            return count;
        }

        public long toKilobytes(long count) {
            return count / DecimalByteUnit.KB;
        }

        public long toMegabytes(long count) {
            return count / DecimalByteUnit.MB;
        }

        public long toGigabytes(long count) {
            return count / DecimalByteUnit.GB;
        }

        public long toTerabytes(long count) {
            return count / DecimalByteUnit.TB;
        }

        public long toPetabytes(long count) {
            return count / DecimalByteUnit.PB;
        }
    },
    KILOBYTES {
        public long convert(long sourceCount, DecimalByteUnit sourceUnit) {
            return sourceUnit.toKilobytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.KB, 9223372036854775L);
        }

        public long toKilobytes(long count) {
            return count;
        }

        public long toMegabytes(long count) {
            return count / DecimalByteUnit.KB;
        }

        public long toGigabytes(long count) {
            return count / DecimalByteUnit.MB;
        }

        public long toTerabytes(long count) {
            return count / DecimalByteUnit.GB;
        }

        public long toPetabytes(long count) {
            return count / DecimalByteUnit.TB;
        }
    },
    MEGABYTES {
        public long convert(long sourceCount, DecimalByteUnit sourceUnit) {
            return sourceUnit.toMegabytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.MB, 9223372036854L);
        }

        public long toKilobytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.KB, 9223372036854775L);
        }

        public long toMegabytes(long count) {
            return count;
        }

        public long toGigabytes(long count) {
            return count / DecimalByteUnit.KB;
        }

        public long toTerabytes(long count) {
            return count / DecimalByteUnit.MB;
        }

        public long toPetabytes(long count) {
            return count / DecimalByteUnit.GB;
        }
    },
    GIGABYTES {
        public long convert(long sourceCount, DecimalByteUnit sourceUnit) {
            return sourceUnit.toGigabytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.GB, 9223372036L);
        }

        public long toKilobytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.MB, 9223372036854L);
        }

        public long toMegabytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.KB, 9223372036854775L);
        }

        public long toGigabytes(long count) {
            return count;
        }

        public long toTerabytes(long count) {
            return count / DecimalByteUnit.KB;
        }

        public long toPetabytes(long count) {
            return count / DecimalByteUnit.MB;
        }
    },
    TERABYTES {
        public long convert(long sourceCount, DecimalByteUnit sourceUnit) {
            return sourceUnit.toTerabytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.TB, 9223372);
        }

        public long toKilobytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.GB, 9223372036L);
        }

        public long toMegabytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.MB, 9223372036854L);
        }

        public long toGigabytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.KB, 9223372036854775L);
        }

        public long toTerabytes(long count) {
            return count;
        }

        public long toPetabytes(long count) {
            return count / DecimalByteUnit.KB;
        }
    },
    PETABYTES {
        public long convert(long sourceCount, DecimalByteUnit sourceUnit) {
            return sourceUnit.toPetabytes(sourceCount);
        }

        public long toBytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.PB, 9223);
        }

        public long toKilobytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.TB, 9223372);
        }

        public long toMegabytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.GB, 9223372036L);
        }

        public long toGigabytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.MB, 9223372036854L);
        }

        public long toTerabytes(long count) {
            return UnitUtil.multiply(count, DecimalByteUnit.KB, 9223372036854775L);
        }

        public long toPetabytes(long count) {
            return count;
        }
    };
    
    private static final long B = 1;
    private static final long GB = 1000000000;
    private static final long KB = 1000;
    private static final long MAX = Long.MAX_VALUE;
    private static final long MB = 1000000;
    private static final long PB = 1000000000000000L;
    private static final long TB = 1000000000000L;
    private static final String[] UNITS = null;

    static {
        UNITS = new String[]{"B", "KB", "MB", "GB", "TB", "PB"};
    }

    public static String format(long bytes) {
        return format(bytes, new DecimalFormat("#,##0.#"));
    }

    public static String format(long bytes, String pattern) {
        return format(bytes, new DecimalFormat(pattern));
    }

    public static String format(long bytes, NumberFormat format) {
        if (bytes < 0) {
            throw new IllegalArgumentException("bytes < 0: " + bytes);
        }
        int unitIndex = 0;
        double count = (double) bytes;
        while (count >= 1000.0d && unitIndex < UNITS.length - 1) {
            count /= 1000.0d;
            unitIndex++;
        }
        return format.format(count) + ' ' + UNITS[unitIndex];
    }

    public long convert(long sourceCount, DecimalByteUnit sourceUnit) {
        throw new AbstractMethodError();
    }

    public long toKilobytes(long count) {
        throw new AbstractMethodError();
    }

    public long toMegabytes(long count) {
        throw new AbstractMethodError();
    }

    public long toGigabytes(long count) {
        throw new AbstractMethodError();
    }

    public long toTerabytes(long count) {
        throw new AbstractMethodError();
    }

    public long toPetabytes(long count) {
        throw new AbstractMethodError();
    }
}
